package com.nulldev.util.networking.npacket4.iprotocols.ncryptx.testing;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.data.Sets.DataSet;
import com.nulldev.util.networking.npacket4.nPacket;
import com.nulldev.util.networking.npacket4.iprotocols.ncryptx.KeyExchangeMethod;
import com.nulldev.util.networking.npacket4.iprotocols.ncryptx.nCryptKeyShare;
import com.nulldev.util.networking.npacket4.iprotocols.ncryptx.nCryptKeyShareMeta;
import com.nulldev.util.networking.rudp.Client;
import com.nulldev.util.networking.rudp.Connection;
import com.nulldev.util.networking.rudp.DataReceivedListener;
import com.nulldev.util.networking.rudp.Message;
import com.nulldev.util.networking.rudp.Server;

public class nCryptXFullTest {

	static class nCryptXClient extends Client implements DataReceivedListener {
		private static final KeyExchangeMethod[] ENABLED_CLIENT_METHODS =
			{ KeyExchangeMethod.NCRYPTX1_NEWHOPE, KeyExchangeMethod.NCRYPTX1_SIDH_DHE, KeyExchangeMethod.PKPK_KEY_EXCHANGE,
					KeyExchangeMethod.NCRYPTX1_DHE_CURVE25519, KeyExchangeMethod.SHARED_KEY_EXCHANGE, KeyExchangeMethod.NCRYPTX1_INSEC_RNG_KEY_GEN };

		public nCryptXClient() {
			super(23211, "127.0.0.1", "cryptX/1.0");
			super.registerClientListener(this);
			super.startClient();
		}

		@Override
		public void connected(Connection connection) {
			sendReliablePacket(new nCryptKeyShareMeta(ENABLED_CLIENT_METHODS));
		}

		private nCryptKeyShare client_keys, server_keys;

		@Override
		public void receivedMessage(Message message, Connection conn) {
			final byte[] o = message.getData();
			try {
				final nPacket np = nPacket.createPacketFromBytes(o);
				switch (np.getPacketID()) {
					case "ncryptx.key_share_meta": {
						final nCryptKeyShareMeta ksm = new nCryptKeyShareMeta(np);
						final List<KeyExchangeMethod> kems = ArrayUtils.toArrayList(ksm.getMethods());
						kems.sort((a, b) -> {
							return a.getRating().compareTo(b.getRating());
						});
						final KeyExchangeMethod selected = kems.get(0);
						final nCryptKeyShare keys = new nCryptKeyShare(selected);
						sendReliablePacket(keys);
						this.client_keys = keys;
						break;
					}
					case "ncryptx.key_share": {
						final nCryptKeyShare ks = new nCryptKeyShare(np);
						this.server_keys = ks;
						break;
					}
					default: {
						JVM.println(np);
						break;
					}
				}
			} catch (Exception e) {
				JVM.errorln(e);
			}
		}

		@Override
		public void disconnected(UUID connectionId) {
		}
	}

	static class nCryptXServer extends Server implements DataReceivedListener {
		private static final KeyExchangeMethod[] ENABLED_SERVER_METHODS =
			{ KeyExchangeMethod.NCRYPTX1_NEWHOPE, KeyExchangeMethod.NCRYPTX1_SIDH_DHE, KeyExchangeMethod.PKPK_KEY_EXCHANGE,
					KeyExchangeMethod.NCRYPTX1_DHE_CURVE25519, KeyExchangeMethod.SHARED_KEY_EXCHANGE, KeyExchangeMethod.NCRYPTX1_INSEC_RNG_KEY_GEN };

		final Map<UUID, DataSet<nCryptKeyShare, nCryptKeyShare>> session = FastMaps.map();

		public nCryptXServer() {
			super(23211, "127.0.0.1", 4, "cryptX/1.0");
			super.registerServerListener(this);
			super.startServer();
		}

		@Override
		public void connected(Connection connection) {
		}

		@Override
		public void receivedMessage(Message message, Connection connection) {
			final byte[] o = message.getData();
			try {
				final nPacket np = nPacket.createPacketFromBytes(o);
				switch (np.getPacketID()) {
					case "ncryptx.key_share_meta": {
						final nCryptKeyShareMeta ksm = new nCryptKeyShareMeta(np);
						sendReliablePacketToClient(new nCryptKeyShareMeta(ENABLED_SERVER_METHODS), connection.getConnectionID());
						break;
					}
					case "ncryptx.key_share": {
						final nCryptKeyShare ks = new nCryptKeyShare(np);
						final nCryptKeyShare keys = new nCryptKeyShare(ks.getMethod());
						session.putIfAbsent(connection.getConnectionID(), new DataSet<nCryptKeyShare, nCryptKeyShare>(ks, keys));
						JVM.println("Registered session with '" + connection.getConnectionID() + "' using '" + ks.getMethod() + "'.");
						sendReliablePacketToClient(keys, connection.getConnectionID());
						break;
					}
					default: {
						JVM.println(np);
						break;
					}
				}
			} catch (Exception e) {
				JVM.errorln(e);
			}
		}

		@Override
		public void disconnected(UUID connectionId) {
			session.remove(connectionId);
		}
	}

	public static void main(String[] args) {
		final nCryptXServer server = new nCryptXServer();
		final nCryptXClient client = new nCryptXClient();
	}

}
